package com.shuai.frame.proxy;

import cn.hutool.core.collection.CollUtil;
import com.shuai.frame.config.XSRpcConfig;
import com.shuai.frame.constant.RpcConstant;
import com.shuai.frame.factory.*;
import com.shuai.frame.fault.retry.RetryStrategy;
import com.shuai.frame.fault.tolerate.TolerateStrategy;
import com.shuai.frame.loadbalancer.LoadBalancer;
import com.shuai.frame.model.RpcRequest;
import com.shuai.frame.model.RpcResponse;
import com.shuai.frame.model.ServiceMetaInfo;
import com.shuai.frame.registry.RegistryCenter;
import com.shuai.frame.serializer.Serializer;
import com.shuai.frame.server.tcp.VertxTcpClient;
import com.shuai.frame.utils.LoadConfigUtils;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;

/**
 * 基于JDK的动态代理
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {

    /**
     * XSRpc框架的全局配置对象
     */
    private static final XSRpcConfig XS_RPC_CONFIG;


    /**
     * 使用指定的序列化器，如果未指定则默认使用JDK序列化器
     */
    private static final Serializer serializer;

    static {
        XS_RPC_CONFIG = LoadConfigUtils.loadConfigToBean(XSRpcConfig.class, "xs-rpc");
        // 动态指定序列化器，用户可在application.properties中配置
        serializer = SerializerFactory.getSerializer(XS_RPC_CONFIG.getSerializer());
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 构造请求对象
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        // 从服务中心加载服务列表
        String registryType = XS_RPC_CONFIG.getRegistryCenterConfig().getRegistryType();
        RegistryCenter registryCenter = RegistryFactory.getRegistryCenter(registryType);
        if (registryCenter == null) {
            log.error("未配置注册中心");
            throw new RuntimeException("未配置注册中心");
        }
        // 服务发现 --开始
        ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
        serviceMetaInfo.setServiceName(rpcRequest.getServiceName());
        serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
        String serviceKey = serviceMetaInfo.getServiceKey();
        List<ServiceMetaInfo> serviceMetaInfos = registryCenter.serviceDiscover(serviceKey);
        // 服务发现 -- 结束
        if (CollUtil.isEmpty(serviceMetaInfos)) {
            log.error("服务-【{}】未配置服务节点", serviceKey);
            throw new RuntimeException("服务-【" + serviceKey + "】未配置服务节点");
        }
        // 服务节点可能有多个，负载均衡选定其中一个
        LoadBalancer loadBalancer = LoadBalancerFactory.getLoadBalancer(XS_RPC_CONFIG.getLoadBalancer());
        HashMap<String, Object> requestParams = new HashMap<>();
        requestParams.put("requestParams", rpcRequest.getMethodName());
        ServiceMetaInfo selectedServiceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfos);
        // 获取重试策略
        RetryStrategy retryStrategy = RetryStrategyFactory.getRetryStrategy(XS_RPC_CONFIG.getRetryStrategy());
        /*
        // 请求方式① HTTP请求
        // 发送请求
        HttpResponse execute = HttpRequest.post(selectedServiceMetaInfo.getServiceAddress())
                .header("Content-Type", "application/json;charset=utf8")
                .body(bytes)
                .execute();
        // 返回响应
        return serializer.deserialize(execute.bodyBytes(), RpcResponse.class).getData();
         */

        // 请求方式②：自定义TCP协议请求
        /*
        Vertx vertx = Vertx.vertx();
        NetClient netClient = vertx.createNetClient();
        CompletableFuture<RpcResponse> completableFuture = new CompletableFuture<>();
        netClient.connect(selectedServiceMetaInfo.getServicePort(), selectedServiceMetaInfo.getServiceHost(), result -> {
            if (result.succeeded()) {
                log.info("连接成功");
                NetSocket socket = result.result();
                // 构造协议消息
                ProtocolMessage<RpcRequest> protocolMessage = new ProtocolMessage<>();
                ProtocolMessage.Header header = new ProtocolMessage.Header();
                header.setMagic(ProtocolConstant.PROTOCOL_MAGIC);
                header.setVersion(ProtocolConstant.PROTOCOL_VERSION);
                header.setMessageType((byte) ProtocolMessageTypeEnum.REQUEST.getCode());
                header.setSerializer((byte) ProtocolMessageSerializerEnum.getEnumByDesc(XSRpcApplicationContext.getXSRpConfig().getSerializer()).getCode());
                header.setRequestId(IdUtil.getSnowflakeNextId());
                protocolMessage.setHeader(header);
                protocolMessage.setBody(rpcRequest);
                // 消息编码成Buffer对象
                try {
                    Buffer buffer = ProtocolMessageEncoder.encode(protocolMessage);
                    // 发送消息
                    socket.write(buffer);
                } catch (IOException e) {
                    throw new RuntimeException("协议消息编码异常");
                }
                socket.handler(buffer -> {
                    ProtocolMessage<RpcResponse> protocolMessage1 = null;
                    try {
                        protocolMessage1 = (ProtocolMessage<RpcResponse>) ProtocolMessageDecoder.decode(buffer);
                    } catch (IOException e) {
                        throw new RuntimeException("协议消息解码异常");
                    }
                    completableFuture.complete(protocolMessage1.getBody());
                });
            } else {
                throw new RuntimeException("连接TCP服务器失败");
            }
        });
        RpcResponse rpcResponse = completableFuture.get();
        // 关闭TCP连接
        netClient.close();
         */
        // ③封装请求客户端
        RpcResponse rpcResponse = null;
        try {
            rpcResponse = retryStrategy.doRetry(() -> {
                // 指定重试策略发送请求
                return VertxTcpClient.doRequest(rpcRequest, selectedServiceMetaInfo);
            });
        } catch (Exception e) {
            // 执行容错机制
            TolerateStrategy tolerateStrategy = TolerateStrategyFactory.getTolerateStrategy(XS_RPC_CONFIG.getTolerateStrategy());
            rpcResponse = tolerateStrategy.doTolerate(null, e);
        }
        // 返回响应数据
        return rpcResponse.getData();
    }
}
